一、引言

   今天我们开始讲“行为型”设计模式的第三个模式,该模式是【迭代器模式】,英文名称是:Iterator Pattern。还是老套路,先从名字上来看看。“迭代器模式”我第一次看到这个名称,我的理解是,迭代是遍历的意思,迭代器可以理解为是遍历某某的工具,遍历什么呢?在软件设计中,当然遍历的是集合对象,所以说迭代器模式是遍历集合的一种通用的算法。如果集合只有一种类型,那这个模式就没用了,就是因为集合对象包含数组、列表,字典和哈希表等各种对象,如果为每一种集合对象都实现一套遍历算法,也不太现实,因此为了解决遍历集合有一个统一的接口这个事情,所以就提出了“迭代器”这个模式。

二、迭代器模式的详细介绍

2.1、动机(Motivate)

   在软件构建过程中,集合对象内部结构常常变化各异。但对于这些集合对象,我们希望在不暴露其内部结构的同时,可以让外部客户代码透明地访问其中包含的元素;同时这种“透明遍历”也为“同一种算法在多种集合对象上进行操作”提供了可能。

 使用面向对象技术将这种遍历机制抽象为“迭代器对象”为“应对变化中的集合对象”提供了一种优雅的方式。

2.2、意图(Intent)

   提供一种方法顺序访问一个聚合对象中的各个元素,而又不暴露该对象的内部表示。                                      ——《设计模式》GoF

2.3、结构图

      

2.4、模式的组成
    
    从迭代器模式的结构图可以看出,它涉及到四个角色,它们分别是:

    (1)、抽象迭代器(Iterator):抽象迭代器定义了访问和遍历元素的接口,一般声明如下方法:用于获取第一个元素的first(),用于访问下一个元素的next(),用于判断是否还有下一个元素的hasNext(),用于获取当前元素的currentItem(),在其子类中将实现这些方法。

    (2)、具体迭代器(ConcreteIterator):具体迭代器实现了抽象迭代器接口,完成对集合对象的遍历,同时在对聚合进行遍历时跟踪其当前位置。

    (3)、抽象聚合类(Aggregate):抽象聚合类用于存储对象,并定义创建相应迭代器对象的接口,声明一个createIterator()方法用于创建一个迭代器对象。

    (4)、具体聚合类(ConcreteAggregate):具体聚合类实现了创建相应迭代器的接口,实现了在抽象聚合类中声明的createIterator()方法,并返回一个与该具体聚合相对应的具体迭代器ConcreteIterator实例。

2.5、迭代器模式的代码实现

    迭代器模式在现实生活中也有类似的例子,比如:在部队中,我们可以让某一队伍当中的某人出列,或者让队列里面的每个人依次报名,其实这个过程就是一个遍历的过程。没什么可说的,具体实现代码如下:

  1 namespace 迭代器模式的实现
  2 {
  3     // 部队队列的抽象聚合类--该类型相当于抽象聚合类Aggregate
  4     public interface ITroopQueue
  5     {
  6         Iterator GetIterator();
  7     }
  8  
  9     // 迭代器抽象类
 10     public interface Iterator
 11     {
 12         bool MoveNext();
 13         Object GetCurrent();
 14         void Next();
 15         void Reset();
 16     }
 17  
 18     //部队队列具体聚合类--相当于具体聚合类ConcreteAggregate
 19     public sealed class ConcreteTroopQueue:ITroopQueue
 20     {
 21         private string[] collection;
 22 
 23         public ConcreteTroopQueue()
 24         {
 25             collection = new string[] { "黄飞鸿","方世玉","洪熙官","严咏春" };
 26         }
 27  
 28         public Iterator GetIterator()
 29         {
 30             return new ConcreteIterator(this);
 31         }
 32  
 33         public int Length
 34         {
 35             get { return collection.Length; }
 36         }
 37  
 38         public string GetElement(int index)
 39         {
 40             return collection[index];
 41         }
 42     }
 43  
 44     // 具体迭代器类
 45     public sealed class ConcreteIterator : Iterator
 46     {
 47         // 迭代器要集合对象进行遍历操作,自然就需要引用集合对象
 48         private ConcreteTroopQueue _list;
 49         private int _index;
 50  
 51         public ConcreteIterator(ConcreteTroopQueue list)
 52         {
 53             _list = list;
 54             _index = 0;
 55         }
 56  
 57         public bool MoveNext()
 58         {
 59             if (_index < _list.Length)
 60             {
 61                 return true;
 62             }
 63             return false;
 64         }
 65  
 66         public Object GetCurrent()
 67         {
 68             return _list.GetElement(_index);
 69         }
 70  
 71         public void Reset()
 72         {
 73             _index = 0;
 74         }
 75  
 76         public void Next()
 77         {
 78             if (_index < _list.Length)
 79             {
 80                 _index++;
 81             }
 82  
 83         }
 84     }
 85  
 86     // 客户端(Client)
 87     class Program
 88     {
 89         static void Main(string[] args)
 90         {
 91             Iterator iterator;
 92             ITroopQueue list = new ConcreteTroopQueue();
 93             iterator = list.GetIterator();
 94  
 95             while (iterator.MoveNext())
 96             {
 97                 string ren = (string)iterator.GetCurrent();
 98                 Console.WriteLine(ren);
 99                 iterator.Next();
100             }
101  
102             Console.Read();
103         }
104     }
105 }

  
三、迭代器模式的实现要点:
    
     1、迭代抽象:访问一个聚合对象的内容而无需暴露它的内部表示。

     2、迭代多态:为遍历不同的集合结构提供一个统一的接口,从而支持同样的算法在不同的集合结构上进行操作。

     3、迭代器的健壮性考虑:遍历的同时更改迭代器所在的集合结构,会导致问题。
    
     3.1】、迭代器模式的优点:

          (1)、迭代器模式使得访问一个聚合对象的内容而无需暴露它的内部表示,即迭代抽象。

          (2)、迭代器模式为遍历不同的集合结构提供了一个统一的接口,从而支持同样的算法在不同的集合结构上进行操作

    3.2】、迭代器模式的缺点:

          (1)、迭代器模式在遍历的同时更改迭代器所在的集合结构会导致出现异常。所以使用foreach语句只能在对集合进行遍历,不能在遍历的同时更改集合中的元素。

     3.3】、迭代器模式的使用场景:

       (1)、访问一个聚合对象的内容而无需暴露它的内部表示。

       (2)、支持对聚合对象的多种遍历。

       (3)、为遍历不同的聚合结构提供一个统一的接口(即, 支持多态迭代)。

四、.NET 中迭代器模式的实现

     在mscorlib程序集里有这样一个命名空间,该命名空间就是:System.Collections,在该命名空间里面早已有了迭代器模式的实现。对于聚集接口和迭代器接口已经存在了,其中IEnumerator扮演的就是迭代器的角色,它的实现如下:

public interface IEnumerator
 {
      object Current
      {
           get;
      }

     bool MoveNext();

     void Reset();
 }


     属性Current返回当前集合中的元素,Reset()方法恢复初始化指向的位置,MoveNext()方法返回值true表示迭代器成功前进到集合中的下一个元素,返回值false表示已经位于集合的末尾。能够提供元素遍历的集合对象,在.Net中都实现了IEnumerator接口。

     IEnumerable则扮演的就是抽象聚集的角色,只有一个GetEnumerator()方法,如果集合对象需要具备跌代遍历的功能,就必须实现该接口。

public interface IEnumerable
{
    IEumerator GetEnumerator();
}

    抽象聚合角色(Aggregate)和抽象迭代器角色(Iterator)分别是IEnumerable接口和IEnumerator接口,具体聚合角色(ConcreteAggregate)有Queue类型, BitArray等类型,代码如下:

  1     public sealed class BitArray : ICollection, IEnumerable, ICloneable
  2     {
  3         [Serializable]
  4         private class BitArrayEnumeratorSimple : IEnumerator, ICloneable
  5         {
  6             private BitArray bitarray;
  7 
  8             private int index;
  9 
 10             private int version;
 11 
 12             private bool currentElement;
 13 
 14             public virtual object Current
 15             {
 16                 get
 17                 {
 18                     if (this.index == -1)
 19                     {
 20                         throw new InvalidOperationException(Environment.GetResourceString("InvalidOperation_EnumNotStarted"));
 21                     }
 22                     if (this.index >= this.bitarray.Count)
 23                     {
 24                         throw new InvalidOperationException(Environment.GetResourceString("InvalidOperation_EnumEnded"));
 25                     }
 26                     return this.currentElement;
 27                 }
 28             }
 29 
 30             internal BitArrayEnumeratorSimple(BitArray bitarray)
 31             {
 32                 this.bitarray = bitarray;
 33                 this.index = -1;
 34                 this.version = bitarray._version;
 35             }
 36 
 37             public object Clone()
 38             {
 39                 return base.MemberwiseClone();
 40             }
 41 
 42             public virtual bool MoveNext()
 43             {
 44                 if (this.version != this.bitarray._version)
 45                 {
 46                     throw new InvalidOperationException(Environment.GetResourceString("InvalidOperation_EnumFailedVersion"));
 47                 }
 48                 if (this.index < this.bitarray.Count - 1)
 49                 {
 50                     this.index++;
 51                     this.currentElement = this.bitarray.Get(this.index);
 52                     return true;
 53                 }
 54                 this.index = this.bitarray.Count;
 55                 return false;
 56             }
 57 
 58             public void Reset()
 59             {
 60                 if (this.version != this.bitarray._version)
 61                 {
 62                     throw new InvalidOperationException(Environment.GetResourceString("InvalidOperation_EnumFailedVersion"));
 63                 }
 64                 this.index = -1;
 65             }
 66         }
 67 
 68         private const int BitsPerInt32 = 32;
 69 
 70         private const int BytesPerInt32 = 4;
 71 
 72         private const int BitsPerByte = 8;
 73 
 74         private int[] m_array;
 75 
 76         private int m_length;
 77 
 78         private int _version;
 79 
 80         [NonSerialized]
 81         private object _syncRoot;
 82 
 83         private const int _ShrinkThreshold = 256;
 84 
 85         [__DynamicallyInvokable]
 86         public bool this[int index]
 87         {
 88             [__DynamicallyInvokable]
 89             get
 90             {
 91                 return this.Get(index);
 92             }
 93             [__DynamicallyInvokable]
 94             set
 95             {
 96                 this.Set(index, value);
 97             }
 98         }
 99 
100         [__DynamicallyInvokable]
101         public int Length
102         {
103             [__DynamicallyInvokable]
104             get
105             {
106                 return this.m_length;
107             }
108             [__DynamicallyInvokable]
109             set
110             {
111                 if (value < 0)
112                 {
113                     throw new ArgumentOutOfRangeException("value", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum"));
114                 }
115                 int arrayLength = BitArray.GetArrayLength(value, 32);
116                 if (arrayLength > this.m_array.Length || arrayLength + 256 < this.m_array.Length)
117                 {
118                     int[] array = new int[arrayLength];
119                     Array.Copy(this.m_array, array, (arrayLength > this.m_array.Length) ? this.m_array.Length : arrayLength);
120                     this.m_array = array;
121                 }
122                 if (value > this.m_length)
123                 {
124                     int num = BitArray.GetArrayLength(this.m_length, 32) - 1;
125                     int num2 = this.m_length % 32;
126                     if (num2 > 0)
127                     {
128                         this.m_array[num] &= (1 << num2) - 1;
129                     }
130                     Array.Clear(this.m_array, num + 1, arrayLength - num - 1);
131                 }
132                 this.m_length = value;
133                 this._version++;
134             }
135         }
136 
137         public int Count
138         {
139             get
140             {
141                 return this.m_length;
142             }
143         }
144 
145         public object SyncRoot
146         {
147             get
148             {
149                 if (this._syncRoot == null)
150                 {
151                     Interlocked.CompareExchange<object>(ref this._syncRoot, new object(), null);
152                 }
153                 return this._syncRoot;
154             }
155         }
156 
157         public bool IsReadOnly
158         {
159             get
160             {
161                 return false;
162             }
163         }
164 
165         public bool IsSynchronized
166         {
167             get
168             {
169                 return false;
170             }
171         }
172 
173         private BitArray()
174         {
175         }
176 
177         [__DynamicallyInvokable]
178         public BitArray(int length) : this(length, false)
179         {
180         }
181 
182         [__DynamicallyInvokable]
183         public BitArray(int length, bool defaultValue)
184         {
185             if (length < 0)
186             {
187                 throw new ArgumentOutOfRangeException("length", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum"));
188             }
189             this.m_array = new int[BitArray.GetArrayLength(length, 32)];
190             this.m_length = length;
191             int num = defaultValue ? -1 : 0;
192             for (int i = 0; i < this.m_array.Length; i++)
193             {
194                 this.m_array[i] = num;
195             }
196             this._version = 0;
197         }
198 
199         [__DynamicallyInvokable]
200         public BitArray(byte[] bytes)
201         {
202             if (bytes == null)
203             {
204                 throw new ArgumentNullException("bytes");
205             }
206             if (bytes.Length > 268435455)
207             {
208                 throw new ArgumentException(Environment.GetResourceString("Argument_ArrayTooLarge", new object[]
209                 {
210                     8
211                 }), "bytes");
212             }
213             this.m_array = new int[BitArray.GetArrayLength(bytes.Length, 4)];
214             this.m_length = bytes.Length * 8;
215             int num = 0;
216             int num2 = 0;
217             while (bytes.Length - num2 >= 4)
218             {
219                 this.m_array[num++] = ((int)(bytes[num2] & 255) | (int)(bytes[num2 + 1] & 255) << 8 | (int)(bytes[num2 + 2] & 255) << 16 | (int)(bytes[num2 + 3] & 255) << 24);
220                 num2 += 4;
221             }
222             switch (bytes.Length - num2)
223             {
224             case 1:
225                 goto IL_103;
226             case 2:
227                 break;
228             case 3:
229                 this.m_array[num] = (int)(bytes[num2 + 2] & 255) << 16;
230                 break;
231             default:
232                 goto IL_11C;
233             }
234             this.m_array[num] |= (int)(bytes[num2 + 1] & 255) << 8;
235             IL_103:
236             this.m_array[num] |= (int)(bytes[num2] & 255);
237             IL_11C:
238             this._version = 0;
239         }
240 
241         [__DynamicallyInvokable]
242         public BitArray(bool[] values)
243         {
244             if (values == null)
245             {
246                 throw new ArgumentNullException("values");
247             }
248             this.m_array = new int[BitArray.GetArrayLength(values.Length, 32)];
249             this.m_length = values.Length;
250             for (int i = 0; i < values.Length; i++)
251             {
252                 if (values[i])
253                 {
254                     this.m_array[i / 32] |= 1 << i % 32;
255                 }
256             }
257             this._version = 0;
258         }
259 
260         [__DynamicallyInvokable]
261         public BitArray(int[] values)
262         {
263             if (values == null)
264             {
265                 throw new ArgumentNullException("values");
266             }
267             if (values.Length > 67108863)
268             {
269                 throw new ArgumentException(Environment.GetResourceString("Argument_ArrayTooLarge", new object[]
270                 {
271                     32
272                 }), "values");
273             }
274             this.m_array = new int[values.Length];
275             this.m_length = values.Length * 32;
276             Array.Copy(values, this.m_array, values.Length);
277             this._version = 0;
278         }
279 
280         [__DynamicallyInvokable]
281         public BitArray(BitArray bits)
282         {
283             if (bits == null)
284             {
285                 throw new ArgumentNullException("bits");
286             }
287             int arrayLength = BitArray.GetArrayLength(bits.m_length, 32);
288             this.m_array = new int[arrayLength];
289             this.m_length = bits.m_length;
290             Array.Copy(bits.m_array, this.m_array, arrayLength);
291             this._version = bits._version;
292         }
293 
294         [__DynamicallyInvokable]
295         public bool Get(int index)
296         {
297             if (index < 0 || index >= this.Length)
298             {
299                 throw new ArgumentOutOfRangeException("index", Environment.GetResourceString("ArgumentOutOfRange_Index"));
300             }
301             return (this.m_array[index / 32] & 1 << index % 32) != 0;
302         }
303 
304         [__DynamicallyInvokable]
305         public void Set(int index, bool value)
306         {
307             if (index < 0 || index >= this.Length)
308             {
309                 throw new ArgumentOutOfRangeException("index", Environment.GetResourceString("ArgumentOutOfRange_Index"));
310             }
311             if (value)
312             {
313                 this.m_array[index / 32] |= 1 << index % 32;
314             }
315             else
316             {
317                 this.m_array[index / 32] &= ~(1 << index % 32);
318             }
319             this._version++;
320         }
321 
322         [__DynamicallyInvokable]
323         public void SetAll(bool value)
324         {
325             int num = value ? -1 : 0;
326             int arrayLength = BitArray.GetArrayLength(this.m_length, 32);
327             for (int i = 0; i < arrayLength; i++)
328             {
329                 this.m_array[i] = num;
330             }
331             this._version++;
332         }
333 
334         [__DynamicallyInvokable]
335         public BitArray And(BitArray value)
336         {
337             if (value == null)
338             {
339                 throw new ArgumentNullException("value");
340             }
341             if (this.Length != value.Length)
342             {
343                 throw new ArgumentException(Environment.GetResourceString("Arg_ArrayLengthsDiffer"));
344             }
345             int arrayLength = BitArray.GetArrayLength(this.m_length, 32);
346             for (int i = 0; i < arrayLength; i++)
347             {
348                 this.m_array[i] &= value.m_array[i];
349             }
350             this._version++;
351             return this;
352         }
353 
354         [__DynamicallyInvokable]
355         public BitArray Or(BitArray value)
356         {
357             if (value == null)
358             {
359                 throw new ArgumentNullException("value");
360             }
361             if (this.Length != value.Length)
362             {
363                 throw new ArgumentException(Environment.GetResourceString("Arg_ArrayLengthsDiffer"));
364             }
365             int arrayLength = BitArray.GetArrayLength(this.m_length, 32);
366             for (int i = 0; i < arrayLength; i++)
367             {
368                 this.m_array[i] |= value.m_array[i];
369             }
370             this._version++;
371             return this;
372         }
373 
374         [__DynamicallyInvokable]
375         public BitArray Xor(BitArray value)
376         {
377             if (value == null)
378             {
379                 throw new ArgumentNullException("value");
380             }
381             if (this.Length != value.Length)
382             {
383                 throw new ArgumentException(Environment.GetResourceString("Arg_ArrayLengthsDiffer"));
384             }
385             int arrayLength = BitArray.GetArrayLength(this.m_length, 32);
386             for (int i = 0; i < arrayLength; i++)
387             {
388                 this.m_array[i] ^= value.m_array[i];
389             }
390             this._version++;
391             return this;
392         }
393 
394         [__DynamicallyInvokable]
395         public BitArray Not()
396         {
397             int arrayLength = BitArray.GetArrayLength(this.m_length, 32);
398             for (int i = 0; i < arrayLength; i++)
399             {
400                 this.m_array[i] = ~this.m_array[i];
401             }
402             this._version++;
403             return this;
404         }
405 
406         public void CopyTo(Array array, int index)
407         {
408             if (array == null)
409             {
410                 throw new ArgumentNullException("array");
411             }
412             if (index < 0)
413             {
414                 throw new ArgumentOutOfRangeException("index", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum"));
415             }
416             if (array.Rank != 1)
417             {
418                 throw new ArgumentException(Environment.GetResourceString("Arg_RankMultiDimNotSupported"));
419             }
420             if (array is int[])
421             {
422                 Array.Copy(this.m_array, 0, array, index, BitArray.GetArrayLength(this.m_length, 32));
423                 return;
424             }
425             if (array is byte[])
426             {
427                 int arrayLength = BitArray.GetArrayLength(this.m_length, 8);
428                 if (array.Length - index < arrayLength)
429                 {
430                     throw new ArgumentException(Environment.GetResourceString("Argument_InvalidOffLen"));
431                 }
432                 byte[] array2 = (byte[])array;
433                 for (int i = 0; i < arrayLength; i++)
434                 {
435                     array2[index + i] = (byte)(this.m_array[i / 4] >> i % 4 * 8 & 255);
436                 }
437                 return;
438             }
439             else
440             {
441                 if (!(array is bool[]))
442                 {
443                     throw new ArgumentException(Environment.GetResourceString("Arg_BitArrayTypeUnsupported"));
444                 }
445                 if (array.Length - index < this.m_length)
446                 {
447                     throw new ArgumentException(Environment.GetResourceString("Argument_InvalidOffLen"));
448                 }
449                 bool[] array3 = (bool[])array;
450                 for (int j = 0; j < this.m_length; j++)
451                 {
452                     array3[index + j] = ((this.m_array[j / 32] >> j % 32 & 1) != 0);
453                 }
454                 return;
455             }
456         }
457 
458         public object Clone()
459         {
460             return new BitArray(this.m_array)
461             {
462                 _version = this._version,
463                 m_length = this.m_length
464             };
465         }
466 
467         [__DynamicallyInvokable]
468         public IEnumerator GetEnumerator()
469         {
470             return new BitArray.BitArrayEnumeratorSimple(this);
471         }
472 
473         private static int GetArrayLength(int n, int div)
474         {
475             if (n <= 0)
476             {
477                 return 0;
478             }
479             return (n - 1) / div + 1;
480         }
481     }

   还有很多类型,就不一一列举了,大家可以查看源代码,每个元素都可以在迭代器模式的构造图上找到对应的元素。

   具体的类型和代码截图如下:

     
  
五、总结

    今天到此就把迭代器模式写完了,该模式不是很难,结构也不是很复杂,Net框架里面也有现成的实现。并且在 Net 2.0里面还有升级的实现,要想学习该模式,可以好好看看该模式在Net 框架中的实现,受益匪浅。

posted on 2017-11-27 13:03  可均可可  阅读(3921)  评论(0编辑  收藏  举报